Skip to content

04 Docker Compose

你的Agent服务需要Flask处理请求、Redis存缓存、Nginx做反向代理。用docker run一个一个启动?要敲三条命令,还要手动创建网络、设置环境变量、保证启动顺序。改一下配置又要全部重来。

Docker Compose就是来解决这个问题的:用一个YAML文件定义整个应用栈,一条命令启动所有服务。

一、compose.yaml

在项目根目录创建compose.yaml文件:

yaml
services:
  web:
    build: .
    ports:
      - "8000:5000"
    environment:
      - REDIS_HOST=redis

  redis:
    image: redis:alpine

这就定义了一个两服务的应用栈:

  • web服务:用当前目录的Dockerfile构建镜像,把容器的5000端口映射到宿主机的8000端口
  • redis服务:直接用redis:alpine镜像

启动:

bash
docker compose up

一条命令,两个容器同时启动,自动创建网络让它们能互相通信。web服务可以直接用redis作为主机名访问Redis服务,不需要知道Redis的IP地址。

二、核心配置

2.1 services

services定义应用中的每个服务。每个服务名就是容器在网络中的主机名。

yaml
services:
  web:
    build: .
    ports:
      - "8000:5000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379

  redis:
    image: redis:alpine

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    depends_on:
      - web

2.2 build

yaml
# 用当前目录的Dockerfile构建
build: .

# 指定Dockerfile路径
build:
  context: .
  dockerfile: Dockerfile.prod

2.3 image

yaml
# 使用已有镜像
image: redis:alpine

# 构建后打标签
build: .
image: yourusername/my-app:1.0

2.4 ports

端口映射,格式和docker run -p一样:

yaml
ports:
  - "8000:5000"     # 宿主机8000 → 容器5000
  - "443:443"       # 宿主机443 → 容器443
  - "127.0.0.1:3000:3000"  # 只允许本机访问

不写宿主机端口,让Docker自动分配:

yaml
ports:
  - "5000"   # Docker随机分配宿主机端口

2.5 environment

设置环境变量:

yaml
environment:
  - REDIS_HOST=redis
  - REDIS_PORT=6379
  - DEBUG=false

也可以用.env文件管理环境变量:

yaml
# .env
APP_PORT=8000
REDIS_HOST=redis
yaml
# compose.yaml
ports:
  - "${APP_PORT}:5000"
environment:
  - REDIS_HOST=${REDIS_HOST}

Compose自动读取同目录下的.env文件。把.env加到.dockerignore里,避免打包进镜像。

2.6 volumes

数据持久化,后面会专门讲:

yaml
services:
  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data

volumes:
  redis-data:

2.7 depends_on

控制服务启动顺序:

yaml
services:
  web:
    build: .
    depends_on:
      - redis

  redis:
    image: redis:alpine

depends_on只保证启动顺序,不保证依赖服务已经准备好。如果web启动时Redis还没初始化完,连接会失败。要解决这个问题,需要用健康检查。

三、健康检查

depends_on加健康检查,才能保证依赖服务真正就绪后再启动:

yaml
services:
  web:
    build: .
    ports:
      - "8000:5000"
    environment:
      - REDIS_HOST=redis
    depends_on:
      redis:
        condition: service_healthy

  redis:
    image: redis:alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s
配置项说明
test健康检查命令,返回0表示健康
interval检查间隔
timeout每次检查的超时时间
retries连续失败几次后标记为不健康
start_period启动宽限期,此期间的失败不计入重试次数

现在Compose会等Redis健康检查通过后才启动web服务。

常用服务的健康检查命令:

yaml
# Redis
healthcheck:
  test: ["CMD", "redis-cli", "ping"]

# PostgreSQL
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres"]

# HTTP服务
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]

四、Compose Watch

开发时每次改代码都要停掉、重建、重启——太烦了。Compose Watch能监听文件变化,自动同步到容器里。

yaml
services:
  web:
    build: .
    ports:
      - "8000:5000"
    develop:
      watch:
        - action: sync+restart
          path: .
          target: /app
        - action: rebuild
          path: requirements.txt

两种同步策略:

策略说明适用场景
sync+restart同步文件到容器内并重启代码改动
rebuild触发镜像重建依赖文件变动

启动时加--watch标志:

bash
docker compose up --watch

现在改完代码保存,Compose自动同步并重启容器,不需要手动操作。改了requirements.txt会触发镜像重建,因为装依赖需要重新构建。

五、常用命令

bash
# 启动所有服务(前台)
docker compose up

# 启动所有服务(后台)
docker compose up -d

# 启动并强制重建镜像
docker compose up --build

# 启动并监听文件变化
docker compose up --watch

# 停止所有服务
docker compose down

# 停止并删除数据卷
docker compose down -v

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f

# 查看单个服务日志
docker compose logs -f web

# 在运行中的容器里执行命令
docker compose exec web bash

# 验证配置文件
docker compose config

六、拆分Compose文件

随着服务增多,一个compose.yaml会变得很长。用include可以把配置拆分到多个文件:

yaml
# compose.yaml
include:
  - path: ./infra.yaml

services:
  web:
    build: .
    ports:
      - "8000:5000"
    depends_on:
      redis:
        condition: service_healthy
yaml
# infra.yaml
services:
  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  redis-data:

所有include的服务共享同一个网络,可以用服务名互相访问。

七、完整的Agent服务compose.yaml

yaml
include:
  - path: ./infra.yaml

services:
  web:
    build: .
    ports:
      - "${APP_PORT}:5000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      redis:
        condition: service_healthy
    develop:
      watch:
        - action: sync+restart
          path: .
          target: /app
        - action: rebuild
          path: requirements.txt
yaml
# infra.yaml
services:
  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

volumes:
  redis-data:

八、总结

Docker Compose的核心就这些:

概念作用
services定义应用的各个服务
build用Dockerfile构建镜像
image使用已有镜像
ports端口映射
environment环境变量
depends_on启动顺序
healthcheck健康检查
volumes数据持久化
develop.watch文件监听和热更新

用Compose管理多容器应用,比手动docker run方便太多了。一条命令启动、一条命令停止、配置全在YAML文件里、版本控制跟着代码走。

下一篇我们深入Docker的网络和存储——容器之间怎么通信、数据怎么持久化。